package com.element.security.single.custom;

import com.common.core.exception.code.SecurityErrorCode;
import com.common.core.results.VoidResult;
import com.element.security.single.constant.SecurityParams;
import com.element.security.single.service.IGrantTypeService;
import com.element.security.single.utils.ResponseUtil;
import lombok.SneakyThrows;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final IGrantTypeService grantTypeService;

    public CustomAuthenticationFilter(IGrantTypeService grantTypeService) {
        this.grantTypeService = grantTypeService;
    }

    @SneakyThrows
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String grantType = getGrantType(request);
        if (!StringUtils.hasText(grantType) || !grantTypeService.getGrantTypes().contains(grantType)) {
            ResponseUtil.writeJson(response, VoidResult.failed(SecurityErrorCode.A_UNSUPPORTED_GRANT_TYPE));
            return null;
        }
        if (grantTypeService.PASSWORD.equals(grantType)) {
            if (request.getMethod().equals("POST")) {
                return super.attemptAuthentication(request, response);
            } else {
                String userName = this.obtainUsername(request);
                String password = this.obtainPassword(request);
                if (userName == null) {
                    userName = "";
                }
                if (password == null) {
                    password = "";
                }
                userName = userName.trim();
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password);
                this.setDetails(request, authRequest);
                return this.getAuthenticationManager().authenticate(authRequest);
            }
        } else {
            Map<String, String> parameters = getParameters(request);
            CustomAuthenticationToken customAuthenticationToken = new CustomAuthenticationToken(parameters);
            return this.getAuthenticationManager().authenticate(customAuthenticationToken);
        }
    }

    private String getGrantType(HttpServletRequest request) {
        return request.getParameter(SecurityParams.GRANT_TYPE);
    }

    private Map<String, String> getParameters(HttpServletRequest request) {
        Map<String, String> paramMap = new LinkedHashMap<>();
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String key = paramNames.nextElement();
            String value = request.getParameter(key);
            if (StringUtils.hasText(key) && StringUtils.hasText(value)) {
                paramMap.put(key, value);
            }
        }
        return paramMap;
    }
}